package com.example.config;

import com.example.filter.CustomFilter;
import com.example.filter.JwtAuthenticationTokenFilter;
import com.example.security.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig {
    //customfilter是直接引入使用，jwt过滤器使用了@Compent自动注册为bean了要引入
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    /**
     * 用户名和密码也可以在application.properties中设置。
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService() {
        // 创建基于内存的用户信息管理器
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        // 创建UserDetails对象，用于管理用户名、用户密码、用户角色、用户权限等内容
        manager.createUser(
                User.withUsername("admin").password("yourpassword").roles("ADMIN").build()
        );
        return manager;
    }
    /**
     * 认证管理。     jwt的用户验证
     * @param authConfig
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    /**
     * 密码加码
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 也可用有参构造，取值范围是 4 到 31，默认值为 10。数值越大，加密计算越复杂
        return new BCryptPasswordEncoder();
    }
    /**
     * 配置过滤链
     * 配置自动注销功能必须在函数里加UserDetailsService userDetailsService,因为重写了使用数据库认证所以用baseuserserviceimpl
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http,  UserDetailsServiceImpl userDetailsService) throws Exception {
        http
                // 开启授权保护，配置请求授权规则
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers("/user/login","/mylogin","/druid/**","/greenHouse/get/average/time").permitAll()   // 不需要认证的地址有哪些  ("/blog/**", "/public/**", "/about")
                        .anyRequest()      // 对所有请求开启授权保护
                        .authenticated()   // 已认证的请求会被自动授权
                )
                // 配置自定义登录页面
                // 本处禁用前端页面，使用功能RESTful风格前后端分离，就是不用登录页面
                .formLogin(form -> form.disable())
                .httpBasic(Customizer -> Customizer.disable())
                // 启用记住我功能。允许用户关闭浏览器后仍然保持登录状态，直到主动注销或者查出设定过期时间
                //.rememberMe(Customizer.withDefaults())
                .rememberMe(rememberMe -> rememberMe
                        .key("uniqueAndSecret") // 设置一个密钥
                        .tokenValiditySeconds(2 * 24 * 60 * 60) // 设置 RememberMe token 的有效期
                        .userDetailsService(userDetailsService) // 显式设置 UserDetailsService
                )
                // 配置注销功能
                .logout(logout -> logout
                        .logoutUrl("/perform_logout") // 自定义注销请求路径
                        //.logoutSuccessUrl("/login?logout=true") // 注销成功后的跳转页面
                        .deleteCookies("JSESSIONID") // 删除指定的 Cookie
                        .permitAll() // 允许所有用户注销
                )
                .sessionManagement(session -> session
                        .sessionFixation(SessionManagementConfigurer.SessionFixationConfigurer::changeSessionId)  // 防止会话固定攻击
                        .maximumSessions(1) // 限制每个用户只能有一个活跃会话
                        .maxSessionsPreventsLogin(false)// 如果为 true，禁止新登录；为 false，允许新登录并终止旧会话
                        .expiredUrl("/login?session=expired") // 当会话过期时跳转到的页面
                )
        ;
        // 关闭 csrf CSRF（跨站请求伪造）是一种网络攻击，攻击者通过欺骗已登录用户，诱使他们在不知情的情况下向受信任的网站发送请求。
        http.csrf(csrf -> csrf.disable());
        // 注册自定义的过滤器CustomFilter
        // 用于jwt 功能确保过滤器的逻辑在每个请求中只执行一次，非常适合需要对每个请求进行处理的场景
        http.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        //授权认证，基于角色在 Spring Security 6.x 版本中，antMatchers() 方法已被移除，取而代之的是使用新的基于 请求匹配器 (RequestMatchers) 的方法
        /*http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/admin/**").hasRole("ADMIN")  // 只有 ADMIN 角色可以访问
                        .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")  // USER 和 ADMIN 角色可以访问
                        .anyRequest().authenticated());  // 其他请求需要认证
        //基于权限的授权，编辑权限还是只读等
        http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/edit/**").hasAuthority("EDIT_PRIVILEGE")  // 仅具有 EDIT_PRIVILEGE 权限的用户可以访问
                        .anyRequest().authenticated());  // 其他请求需要认证*/
        return http.build();
    }
}
